package minijvm.engine;

import minijvm.cmd.ByteCodeCommand;
import minijvm.method.Method;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @author ChangLiang
 * @date 2020/4/27
 */
public class StackFrame {

    /**
     * 模拟 栈帧中的局部变量表
     */
    private List<JavaObject> localVariableTable = new ArrayList<>();

    /**
     * 模拟 栈帧中的操作数栈
     */
    private Stack<JavaObject> operandStack = new Stack<>();

    /**
     * 模拟程序计数器 代表执行到第几个cmd
     * 每个栈帧保存自己的程序计数器
     */
    private int index = 0;

    /**
     * 一个方法 生成 一个栈帧
     */
    private Method method;

    /**
     * 栈帧中还要保存一个调用者栈帧 即这个栈帧的上一个栈帧
     * 当 当前栈帧有返回值，要传回给上一个栈帧的时候 需要需用
     */
    private StackFrame callerFrame = null;

    private StackFrame(Method method) {
        this.method = method;
    }

    public void setLocalVariableTable(List<JavaObject> localVariableTable) {
        this.localVariableTable = localVariableTable;
    }

    public Stack<JavaObject> getOperandStack() {
        return this.operandStack;
    }

    public Method getMethod() {
        return method;
    }

    /**把javaObject放到局部变量表的指定slot中
     *
     * @param slot
     * @param javaObject
     */
    public void setLocalVariableValue(int slot, JavaObject javaObject) {
        if (this.localVariableTable.size() - 1 < slot) {
            for (int i = this.localVariableTable.size(); i <= slot; i++) {
                this.localVariableTable.add(null);
            }
        }
        this.localVariableTable.set(slot, javaObject);
    }

    /**
     * 获取局部变量表slot中的内容
     * @param index slot number
     * @return
     */
    public JavaObject getLocalVariableValue(int index) {
        return this.localVariableTable.get(index);
    }

    public void setCallerFrame(StackFrame callerFrame) {
        this.callerFrame = callerFrame;
    }

    public StackFrame getCallerFrame() {
        return callerFrame;
    }

    /**
     * 调用方法，会创建栈帧
     * @param m
     * @return
     */
    public static StackFrame create(Method m) {
        StackFrame frame = new StackFrame(m);
        return frame;
    }

    /**
     * 执行栈帧：一条一条执行断句后的字节码命令
     * @return
     */
    public ExecutionResult execute() {
        ByteCodeCommand[] cmds = method.getCmds();
        ExecutionResult result = null;
        while (index < cmds.length) {
            // 创建新的执行结果
            result = new ExecutionResult();
            // 默认执行下一条命令
            result.setNextAction(ExecutionResult.RUN_NEXT_CMD);

            ByteCodeCommand cmd = cmds[index];
            System.out.println(cmd.getOffset() + ":" + cmd.getReadableCodeText());
            // 执行command
            cmd.execute(this,result);

            if (result.isRunNextCmd()) {
                index++;
            } else if (result.isExitCurrentFrame()) {
                return result;
            } else if (result.isPauseAndRunNewFrame()) {
                index++;
                // return 返回到执行引擎
                return result;
            } else if (result.isJump()) {
                // 计算出下一条指令的offset对应的是第几个cmd
                int offset = result.getNextCmdOffset();
                index = getNextCommandIndex(offset);
            } else {
                index++;
            }
        }
        return result;
    }

    /**
     * 获取下一条执行command的 行号
     * @param offset
     * @return
     */
    private int getNextCommandIndex(int offset) {
        // 获取method Code attribute中的所有commands
        ByteCodeCommand[] cmds = method.getCodeAttr().getCmds();
        for (int i = 0; i < cmds.length; i++) {
            if (cmds[i].getOffset() == offset) {
                return i;
            }
        }
        throw new RuntimeException("cannot find next command");
    }

}
